Homenagem a William Playfair¶

Nesse notebook, recriamos um dos gráficos de mais famosos de William Playfair, o inventor do gráfico de linha, área e barras, usando ferramentas atuais como Pandas e Plotly.

Como tarefa 2 do curso, devíamos recriar uma visualização histórica. Para isso, escolhi a visualização de um dos gráficos de William Playfair - o inventor do gráfico de linha, área e barras, bem como posteriormente do gráfico de pizza. O gráfico, feito em 1821, mostra a evolução da economia da Inglaterra através da representação visual do preço do trigo e do salário médio semanal de um mecânico.

Nessa tarefa, o principal desafio foi desenvolver a habilidade de dobrar as ferramentas modernas para criar uma visualização específica já determinada, imitando a liberdade que o autor original possuia. Por isso, muitas técnicas não convecionais precisaram ser utilizadas para criar peças gráficas que se assemelhassem à original. Dentre estas, podemos destacar:

  • O gradiente no gráfico de barras foi criado a partir da sobreposição de vários gráficos de barras diferentes, com cores e pesos diferentes. Quanto mais leve a cor, menor a proporção pela qual o eixo y seria multiplicado, criando assim um gradiente onde a cor mais escura fica sempre no topo.
  • Diferentes bases de dados para o gráfico de barras e o de área foram necessárias pois o gráfico de barras assume valores apenas para intervalos de 5 anos, enquanto o gráfico de área tem variação de valores todos os anos.
  • As linhas verticais e horizontais são mini gráficos de linha criados apenas com pontos final e inicial definidos, a fim de imitar as divisões criadas pelo autor no gráfico original.
  • A timeline dos reinados da Inglaterra são na verdade uma sequência (bem comprida) de figuras retangulares e textos com os nomes do respectivos monarcas, imitando a figura desenhada a mão por William Playfair.

Apesar das gambiarras, espero que goste haha :D

In [1]:
# import tools and get data
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px

df = pd.read_csv('Playfair.csv', sep=';')
for i in df.columns:
    df[i] = df[i].str.replace(',', '.').astype(float)
df['year'] = df.index*5 + 1565
df = df.drop(columns=['Wages'])


df_wages = pd.read_csv('Playfair_wages.csv', sep=';')
for i in df_wages.columns:
    df_wages[i] = df_wages[i].str.replace(',', '.').astype(float)
df_wages['year'] = df_wages.index + 1565
In [2]:
# plot the bars for the wheat prices
def create_gradient(df, col):

    # at first, add a bar to set the color of the legend
    fig.add_trace(
        go.Bar(
            x=df['year'],
            y=df[col],
            marker=dict(
                color='#4a6082',
                line=dict(width=0)
            ),
            name = 'Wheat price',
            legendgroup = 'Wheat price',
        )
    )

    colors = ["#4a6082", "#5f7393", "#7486a3", "#c5cedb", "#e2e7ed"]
    for i in range(5):

        new_df = df.copy()
        new_df[col] = new_df[col]*(((5-i)/5)**(3/5))
        fig.add_trace(
            go.Bar(
                x=new_df['year'],
                y=new_df[col],
                marker=dict(
                    color=colors[i],
                    line=dict(width=1, color=colors[i])
                ),
                hoverinfo = 'none',
                showlegend=False,
                legendgroup = 'Wheat price'
            )
        )
        
    # at last, add a transparent bar for hoverinfo
    fig.add_trace(
        go.Bar(
            x=df['year'],
            y=df[col],
            marker=dict(
                color='rgba(0,0,0,0)',
                line=dict(width=0)
            ),
            hovertext = [f"<b>Year:</b> {df['year'][i]}; <br>\
<b>Price:</b> ${df['Weat'][i]}." for i in df.index],
            name = 'Wheat price',
            showlegend=False,
            legendgroup = 'Wheat price',
        )
    )
        
In [3]:
# plot the area for the wages
def create_area(df, col):

    fig.add_trace(
        go.Scatter(
            x=df['year'],
            y=df[col],
            mode='lines',
            line=dict(width=2, color='#ff6e61'),
            fillcolor = '#b8d7e6',
            stackgroup='one',
            name = "Wages",
            hovertext = [f"<b>Year:</b> {df['year'][i]}; <br>\
<b>{col}:</b> ${df[col][i]}." for i in df.index],
        )
    )
In [4]:
# create the lines in the figure
def create_lines(fig):
    # create vertical lines
    for i in [1600, 1650, 1700, 1750, 1800]:
        fig.add_trace(
            go.Scatter(
                x=[i, i],
                y=[0, 100],
                mode='lines',
                line=dict(width=1, color='black'),
                showlegend=False,
                hoverinfo='none'
            )
        )
    
    # create horizontal lines
    for i in [0, 20, 40, 60, 80, 100]:
        fig.add_trace(
            go.Scatter(
                x=[1565, 1821],
                y=[i, i],
                mode='lines',
                line=dict(width=1, color='gray'),
                showlegend=False,
                hoverinfo='none'
            )
        )
In [5]:
# create the reigns timeline in the top of the figure and suplementar text
def create_timeline(fig):
    fig.add_annotation(
        x=1750,
        y=8,
        text="<i><b>Weekly Wages of a Good Mechanic</i></b>",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1565,
        y0=97,
        x1=1603,
        y1=96,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1585,
        y=92,
        text="Elizabeth I",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )
    
    fig.add_shape(
        type="rect",
        x0=1603,
        y0=96,
        x1=1625,
        y1=95,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1615,
        y=91,
        text="James I",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1625,
        y0=97,
        x1=1649,
        y1=96,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1637,
        y=92,
        text="Charles I",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1649,
        y0=96,
        x1=1660,
        y1=95,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1655,
        y=91,
        text="Commonwealth",
        showarrow=True,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1660,
        y0=97,
        x1=1685,
        y1=96,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1673,
        y=92,
        text="Charles II",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1685,
        y0=96,
        x1=1702,
        y1=95,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1694,
        y=91,
        text="James II",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1702,
        y0=97,
        x1=1714,
        y1=96,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1708,
        y=91,
        text="William III",
        showarrow=True,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1714,
        y0=96,
        x1=1727,
        y1=95,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1721,
        y=91,
        text="Anne",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )

    fig.add_shape(
        type="rect",
        x0=1727,
        y0=97,
        x1=1760,
        y1=96,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1744,
        y=92,
        text="George I",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
        
    )

    fig.add_shape(
        type="rect",
        x0=1760,
        y0=96,
        x1=1820,
        y1=95,
        fillcolor="black",
    )
    fig.add_annotation(
        x=1790,
        y=91,
        text="George III",
        showarrow=False,
        font=dict(
            size=12,
            color="black"
        )
    )
In [6]:
# create the elipse title
def create_title(fig):
    fig.add_shape(
        type="circle",
        x0=1598,
        y0=91,
        x1=1702,
        y1=54,
        fillcolor="white",
        line_color="black",
        line_width=2,
    )
    fig.add_annotation(
        x=1650,
        y=87,
        text="Chart",
        showarrow=False,
        font=dict(
            size=30,
            color="black"
        )
    )
    fig.add_annotation(
        x=1650,
        y=70,
        text="""
Showing at One View <br> \
The price of the Quarter of the Wheat <br> \
& Wages of labor by Week. <br> \
--- From --- <br> \
The year 1565 to 1821 <br> \
--- By --- <br> \
<b>William Playfair</b>""",
        showarrow=False,
        font=dict(
            size=9,
            color="black"
        )
    )
In [7]:
fig = go.Figure()
# create bar plot with gradient for Weat
create_gradient(df, 'Weat')
# create area plot for Wages
create_area(df_wages, 'Wages')
# create vertical and horizontal lines
create_lines(fig)
# create texts
create_timeline(fig)
# create title
create_title(fig)

# set figure layout
fig.update_layout(
    xaxis_range=[1565, 1821],
    yaxis_range=[0, 100],
    barmode='overlay',
    bargap=0,
    autosize=False,
    width=750,
    height=500,
    font=dict(
        family="Times New Roman",
    ),
)
fig.show()